package cn.touna.jss.modules.third.baiqishi;

import cn.touna.risk.api.enums.SystemErrorCodeEnum;
import cn.touna.jss.common.factory.ResponseFactory;
import cn.touna.jss.common.factory.RestTemplateConfig;
import cn.touna.jss.common.properties.disconf.BaiqishiProperties;
import cn.touna.jss.common.util.HttpUtil;
import cn.touna.jss.common.util.ValidUtil;
import cn.touna.jss.modules.adapter.ApiMapping;
import cn.touna.jss.modules.third.baiqishi.constant.BaiqiConstant;
import cn.touna.jss.modules.third.baiqishi.entity.BaiqishiRequestEntity;
import cn.touna.jss.modules.third.baiqishi.entity.IpAttribution;
import cn.touna.jss.modules.third.baiqishi.entity.IpAttributionReq;
import cn.touna.jss.modules.third.baiqishi.service.BaiqishiDbService;
import cn.touna.risk.api.domain.Response;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;

/**
 * Created by Steve.Tao on 2017/8/18.
 */
@Service
public class BaiqishiService {
    private static final Logger logger = LogManager.getLogger(BaiqishiService.class);

    @Autowired
    private RestTemplateConfig restTemplateConfig;

    @Autowired
    private BaiqishiProperties baiqishiProperties;

    @Autowired
    private BaiqishiDbService baiqishiDbService;
    /**
     * 风险决策
     *
     * @param request
     * @return
     */
    @ApiMapping(value = "baiqishi.riskDecision" ,desc = "白骑士.风险决策")
    public Response riskDecision(BaiqishiRequestEntity.RiskDecisionRequest request) {
        Response response = ValidUtil.valid(request);
        if(response != null)
            return response;

        JSONObject params = (JSONObject) JSONObject.toJSON(request);
        params.put("verifyKey", baiqishiProperties.getVerifyKey());
        params.put("partnerId", baiqishiProperties.getPartnerId());
        //如果传了appId，则使用指定的appId,否则使用默认的
        if(!StringUtils.isEmpty(request.getAppId())){
            params.put("appId","fast_loan".equals(request.getAppId()) ? baiqishiProperties.getAppId() : request.getAppId());
        } else {
            params.put("appId", baiqishiProperties.getAppId());
        }
        params.put("eventType","amount".equals(request.getEventType()) ? "loan" : request.getEventType());
        params.put("certNo", request.getIdCard());
        params.put("email", request.getEmail());
        params.put("loc_afMobile", request.getLocAfMobile());
        params.put("loc_ssMobile", request.getLocSsMobile());

        params.remove("occurTime");
//        return post(baiqishiProperties.getRiskDecisionUrl(), params.toJSONString());
        try {
            String res = invoke(params, baiqishiProperties.getRiskDecisionUrl());
            if (!StringUtils.isEmpty(res)) {
                return ResponseFactory.success(res);
            }
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR);
        } catch (IOException e) {
            logger.error(e.getMessage(),e);
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_CALL_EXTSYSTEM_ERROR, e.getMessage());
        }
    }

    /**
     * 设备信息查询接口
     * @param request
     * @return
     */
    @ApiMapping(value="baiqishi.queryDeviceId", desc="白骑士.设备信息查询接口")
    public Response queryDeviceId(BaiqishiRequestEntity.RiskDecisionRequest request) {

        if(null == request ||
                StringUtils.isEmpty(request.getTokenKey()) ||
                StringUtils.isEmpty(request.getPlatform())){
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_PARAM_CHECK_FAILED);
        }

        JSONObject requestData = new JSONObject();
        requestData.put("partnerId", baiqishiProperties.getPartnerId());
        requestData.put("verifyKey", baiqishiProperties.getVerifyKey());
        requestData.put("platform", request.getPlatform());
        requestData.put("tokenKey", request.getTokenKey());
        try {
            String res = HttpUtil.postJson(baiqishiProperties.getDeviceFingerPrintUrl(),requestData);
            JSONObject resObj = JSONObject.parseObject(res);
            String resultCode = resObj.getString("resultCode");
            if("BQS000".equals(resultCode)){
                return ResponseFactory.success(resObj);
            }else{
                String msg = resObj.getString("resultDesc");
                return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL,resultCode + ":"+ msg);
            }
        }catch (Exception e) {
            logger.error(e.getMessage(),e);
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_SYSTEM_ERROR,e.getMessage());
        }
    }

    private volatile RestTemplate restTemplate = null;

    HttpRequestRetryHandler httpRequestRetryHandler = null;

    public BaiqishiService() {

        RequestConfig.Builder configBuilder = RequestConfig.custom();
        // 设置连接超时
        configBuilder.setConnectTimeout(120000);
        // 设置读取超时
        configBuilder.setSocketTimeout(120000);
        // 设置从连接池获取连接实例的超时
        configBuilder.setConnectionRequestTimeout(120000);
        // 在提交请求之前 测试连接是否可用
        configBuilder.setStaleConnectionCheckEnabled(true);
        requestConfig = configBuilder.build();

        httpRequestRetryHandler = new HttpRequestRetryHandler() {

            public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
                if (executionCount >= 3) {
                    return false;
                }
                if (exception instanceof InterruptedIOException) {
                    return true;
                }
                return false;
            }
        };
    }

    private static RequestConfig requestConfig;

    public String invoke(Map<String, Object> params, String apiUrl) throws IOException {
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setDefaultRequestConfig(requestConfig)
                .setRetryHandler(httpRequestRetryHandler).build();
        HttpPost httpPost = new HttpPost(apiUrl);
        CloseableHttpResponse response = null;
        org.apache.http.HttpEntity entity;

        try {
            httpPost.setConfig(requestConfig);
            StringEntity se = new StringEntity(JSON.toJSONString(params), "UTF-8");
            httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
            se.setContentType("text/json");
            httpPost.setEntity(se);
            response = httpClient.execute(httpPost);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != HttpStatus.SC_OK) {
                logger.warn("[BqsApiInvoker] invoke failed, response status: " + statusCode);
                return null;
            }
            entity = response.getEntity();
            if (entity == null) {
                logger.warn("[BqsApiInvoker] invoke failed, response output is null!");
                return null;
            }
            String result = EntityUtils.toString(entity, "utf-8");
            return result;
        } catch (Exception e) {
            logger.error("[BqsApiInvoker] invoke throw exception, details: ", e);
        } finally {
            httpPost.releaseConnection();
            if (httpClient != null) {
                httpClient.close();
            }
        }
        return null;
    }

    private static SSLConnectionSocketFactory createSSLConnSocketFactory() {
        SSLConnectionSocketFactory sslsf = null;
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {

                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();
            sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {

                @Override
                public boolean verify(String arg0, SSLSession arg1) {
                    return true;
                }

                @Override
                public void verify(String host, SSLSocket ssl) throws IOException {
                }

                @Override
                public void verify(String host, X509Certificate cert) throws SSLException {
                }

                @Override
                public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
                }
            });
        } catch (GeneralSecurityException e) {
            logger.error(e.getMessage(), e);
        }
        return sslsf;
    }

    private Response post(String url, String body) {
        if (restTemplate == null) {
            StringHttpMessageConverter converter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
            restTemplate = restTemplateConfig.build(true, converter);
        }

//        HttpHeaders headers = new HttpHeaders();
//        headers.set("Content-Type","application/json");
//        headers.setContentType(MediaType.parseMediaType("text/json"));
//        headers.add("Accept", MediaType.APPLICATION_JSON.toString());
        HttpHeaders headers = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(type);
        headers.add("Accept", MediaType.APPLICATION_JSON.toString());
        HttpEntity<String> formEntity = new HttpEntity<String>(body, headers);
        String str = restTemplate.postForObject(url, formEntity, String.class);
        try {
            JSONObject res = JSONObject.parseObject(str);
            String resultCode = res.getString("resultCode");
            if ("BQS000".equals(resultCode)) {
                return ResponseFactory.success(str);
            } else {
                String resultDesc = res.getString("resultDesc");
                logger.info("调用白骑士风险决策接口错误,message={}", resultDesc);
                return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR, resultDesc);
            }
        } catch (Exception e) {
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR, e.getMessage());
        }
    }

    @ApiMapping(value="baiqishi.deviceFingerPrint",desc="白骑士.设备指纹信息")
    public Response deviceFingerPrint(BaiqishiRequestEntity.DeviceFingerPrintRequest request) {
        Response response = ValidUtil.valid(request);
        if(response != null)
            return response;
        JSONObject params = (JSONObject) JSONObject.toJSON(request);
        params.put("verifyKey", baiqishiProperties.getVerifyKey());
        params.put("partnerId", baiqishiProperties.getPartnerId());
        params.put("appId", baiqishiProperties.getAppId());

        return post(baiqishiProperties.getDeviceFingerPrintUrl(), params.toJSONString());
    }


    @ApiMapping(value="baiqishi.gpscountrycheck", desc="白骑士.gps归属地查询")
    public Response gpscountrycheck(BaiqishiRequestEntity.Location location){
        JSONObject extParam = new JSONObject();
        extParam.put("longitude",String.valueOf(location.getLongitude()));
        extParam.put("latitude",String.valueOf(location.getLatitude()));

        JSONObject requestData = new JSONObject();
        requestData.put("partnerId", baiqishiProperties.getPartnerId());
        requestData.put("verifyKey", baiqishiProperties.getVerifyKey());
        requestData.put("extParam",extParam);

        try {
            String res = HttpUtil.postJson(baiqishiProperties.getGpscountrycheckUrl(),requestData);
            JSONObject resObj = JSONObject.parseObject(res);
            String resultCode = resObj.getString("resultCode");
            if("BQS000".equals(resultCode)){
                return ResponseFactory.success(resObj);
            }else{
                String msg = resObj.getString("resultDesc");
                return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL,resultCode + ":"+ msg);
            }
        }catch (Exception e) {
            logger.error(e.getMessage(),e);
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_SYSTEM_ERROR,e.getMessage());
        }
    }

    @ApiMapping(value="baiqishi.ipAttribution",desc="白骑士.ip归属地信息查询接口")
    public Response getIpAttribution(IpAttributionReq req){
        logger.info("白骑士IP归属地信息接口(getIpAttribution)调用 begin:"+req.toString());

        if(null==req || StringUtils.isEmpty(req.getIp())){return ResponseFactory.fail(SystemErrorCodeEnum.JSS_SYSTEM_ERROR.getErrorCode(),"ip信息不允许为空");}
        String ip=req.getIp();
        JSONObject requestData = new JSONObject();
        requestData.put("partnerId", baiqishiProperties.getPartnerId());
        requestData.put("verifyKey", baiqishiProperties.getVerifyKey());
        JSONObject extParam = new JSONObject();
        extParam.put("ip",String.valueOf(ip.trim()));
        requestData.put("extParam",extParam);
        try {
            String result = HttpUtil.postJson(baiqishiProperties.getIpCountrycheckUrl(),requestData);
            logger.info("白骑士IP归属地信息接口返回结果:"+result);
            return parseResult(ip,result);
        }catch (Exception e) {
            logger.error(e.getMessage(),e);
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_SYSTEM_ERROR,e.getMessage());
        }
    }

    private Response parseResult(String ip,String result) {
        JSONObject resObj = JSONObject.parseObject(result);
        String resultCode = resObj.getString("resultCode");
        if(BaiqiConstant.SUCC_CODE.equals(resultCode)){
            saveIpAttribution(ip,resObj);
            return ResponseFactory.success(resObj);
        }else{
            String msg = resObj.getString("resultDesc");
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_FAIL,resultCode + ":"+ msg);
        }
    }
    /**
     ********************************************************************
     * 检查ip归属地数据是否存在,不存在即可保存入库
     * @param ip ip地址
     * @param resObj 归属地信息
     * @Author  zhangjialian
     * @return  void
     * @date    2018/8/24 18:51
     ********************************************************************/
    private void saveIpAttribution(String ip,JSONObject resObj) {
        JSONObject resultData=resObj.getJSONObject("resultData");
        if(null!=resultData){
            IpAttribution ipData=resultData.getObject("ipcountryDetail",IpAttribution.class);
            if(null!=ipData){
                ipData.setIp(ip);
                baiqishiDbService.saveIpAttribution(ipData);
            }
        }
    }

}
